Instead of a monolithic product, modern executable software is typically a dynamic collection of a multitude of independent and often interrelated computer-executable program modules. These modules are executable procedures (e.g., routines) which perform one or more defined functions. In the complex, intertwined, and interconnected conglomeration of executable procedures, procedures call each other. For example, one procedure calls another which calls two more which calls another and another and two more and so forth.
Each procedure typically has a set of input values. One of the most common ways for a procedure to acquire its input values is via arguments (i.e., parameters) passed along to it when it is called.
For example, consider the following procedure calls and the defined syntax to call them:
PROC1 (a, b)
PROC2 (a, b)
PROC3 (a, b, c, d)
where the upper-case name (e.g., PROC1) represents the name of the procedure and the lower-case letters in parenthesis represents arguments passed to those procedures when they are invoked. For this example, assuming that all specified values of all specified arguments are necessary for that procedure's functionality and the procedure calling it does not produce that value.
When PROC1 calls PROC2, PROC1 is able to supply the values for “a” and “b” since they were supplied when PROC1 was called. However, when PROC2 subsequently calls PROC3, there is a problem. PROC2 does not have a value for “c” or “d” (because it did not get those values from PROC1 when it was called).
As illustrated by the example, the problem is how to best access a set of data (and, perhaps, sub-procedures) that might be useful at some point during a logical execution flow though the complex, intertwined, and interconnected conglomeration of executable procedures of modern software product(s).
One traditional approach is to solving this problem is to modify the calling definitions of procedures to include currently useless arguments that may be useful later if other procedures that use those arguments are called.
Using the above example, the procedure invocation definitions might be changed to this [the added arguments being italicized]:
PROC1 (a, b, c, d)
PROC2 (a, b, c, d)
PROC3 (a, b, c, d)
The definitions of PROC1 and PROC2 are changed here so that they now include arguments “c” and “d.” While these newly supplied arguments are unnecessary for PROC1 and PROC2, they are necessary for PROC3.
The primary drawback to this approach is that one small change causes a ripple effect of definitional changes across the “ocean” of currently defined procedures. In other words, the simple addition of one new procedure (or added feature to an existing procedure) forces a redefinition of the procedure calls for all of the procedures which might ever be in the execution chain of those that might ever call this new procedure (or the altered existing one). In some cases there might a new procedure-1 that calls another new procedure-2 that requires additional information, but the actual call from procedure-1 might have pass through a series of existing old procedures before reaching procedure-2, and it may not be possible to change these existing procedures to add these extra arguments due to various reasons, for example there might be other callers of these existing procedures that will break if new arguments are added.
Another traditional approach to solving this problem is to utilize global variable definitions, which are accessible by a global set of procedures. Global variables are used here to avoid having to pass frequently-used variables continuously throughout a chain of called procedures.
In computer programming, a global variable is a variable that does not belong to any procedure in particular and can therefore be accessed from any procedure (within a defined group). Thus use of global variable allows for flexibility.
However, generally speaking, use of global variables is considered bad practice. A global variable can potentially be modified from anywhere. This allows for unexpected results. Also, because any part of an executable program may depend on the global variable, there is an unlimited potential for creating mutual dependencies. Adding mutual dependencies increases complexity.
Furthermore, global variable may not be accessible across the full scope of procedures executed along the logical execution path. For example, the one set of global variable may be accessible by set of procedures of a given application, but they might not be global (and thus be inaccessible) to another set of procedures of the operating system or another application.
These conventional solutions to this problem are widely used, but, as shown above, they each have drawbacks.